<!--
Copyright 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="./patch-summary-list.html">
<link rel="import" href="../lib/cqstats-util.html">
<link rel="import" href="../lib/log.html">
<link rel="import" href="../lib/net.html">
<link rel="import" href="../lib/sugar.html">

<script>
(function(){

this.PatchSummary = function PatchSummary(value, unit, issue, patchset, alertThreshold) {
  this.value = value;
  this.unit = unit;
  this.issue = issue;
  this.patchset = patchset;
  this.alertThreshold = alertThreshold;
  this.list = null;

  var reviewHost = 'https://codereview.chromium.org';
  this._patchURL = '{1}/{2}#ps{3}'.assign(reviewHost, this.issue, this.patchset);
  this._issueTitle = null;
  var logHost = '//chromium-cq-status.appspot.com';
  this._statusURL = '{1}/patch-status/{2}/{3}'.assign(
      logHost, this.issue, this.patchset);
  this._recentURL = '{1}/recent#issue={2},patchset={3}'.assign(
      logHost, this.issue, this.patchset);
  this._columnValues = [];
  this._jobViewBuilderBuilds = {};
  this._summaryPromise = cqStatsUtil.loadPatchSummary(issue, patchset);
  this._summary = null;

  var self = this;
  var issueURL = '{1}/api/{2}'.assign(reviewHost, this.issue);
  net.json({url: issueURL, cache: true}).then(function(json) {
    self._issueTitle = json.subject;
  }).catch(log);
  self._summaryPromise.then(function(summary) {
    self._summary = summary;

    summary.attempts.forEach(function(attempt, i) {
      attempt.number = summary.attempts.length - i;
      attempt.summaryText = getAttemptSummaryText(attempt);
    });

    self._columnValues = PatchSummary.summaryColumns.map(function(column) {
      return column.getter(summary);
    });

    var jobViews = PatchSummaryList.viewMenu.getSubMenu('jobs').getSubNames();
    for (var jobView of jobViews) {
      self._jobViewBuilderBuilds[jobView] = getBuilderBuilds(summary, jobView);
    };
  }).catch(log);
};

PatchSummary.prototype.getSummary = function() {
  return this._summaryPromise;
}

function getJobs(summary, jobView) {
  switch (jobView) {
    case 'flaky':
      return summary.flaky_jobs;
    case 'all':
      return summary.attempts.map(function(attempt) {
        return Object.values(attempt.jobs).flatten();
      }).flatten();
    default:
      return summary.attempts.map(function(attempt) {
        return attempt.jobs[jobView];
      }).flatten();
  }
}

function sortBy(array, field) {
  array.sort(function(itemA, itemB) {
    var a = itemA[field];
    var b = itemB[field];
    return a > b ? 1 : (a < b ? -1 : 0);
  });
}

function getBuilderBuilds(summary, jobView) {
  var jobs = getJobs(summary, jobView);
  console.assert(jobs);

  var buildMap = new Map();
  for (var job of jobs) {
    var builderKey = job.master + ':' + job.builder;
    if (!buildMap.has(builderKey)) {
      buildMap.set(builderKey, {
        name: job.builder,
        master: job.master,
        builds: [],
      });
    }
    buildMap.get(builderKey).builds.push({
      number: job.build_number,
      slave: job.slave,
      url: job.url,
      state: job.state,
    });
  }

  var builders = [];
  buildMap.forEach(function(builder) {
    sortBy(builder.builds, 'number');
    builders.push(builder);
  });
  sortBy(builders, 'name');
  return builders;
}

function secondsToMinutes(seconds) {
  return (seconds / 60).toFixed(0);
}

function getAttemptSummaryText(attempt) {
  var successText = (function(){
    switch (attempt.success) {
      case true:
        return 'Success';
      case null:
        return 'Running';
      default:
        var text = 'Fail';
        var reason = attempt.fail_reason;
        if (reason && reason.fail_type) {
          text += ': ';
          if (reason.fail_type === 'failed_jobs') {
            var getBuilder = function(detail) { return detail.builder };
            text += reason.fail_details.map(getBuilder).join(' ');
          } else {
            text += reason.fail_type;
          }
        }
        return text;
    }
  })();
  return '{1} ({2}min)'.assign(
      successText, secondsToMinutes(attempt.durations.total));
}

PatchSummary.summaryColumns = [
  {
    header: 'False rejects',
    getter: function(summary) {
      return summary.success ? summary.attempt_fail_count : 0;
    },
  },
  {
    header: 'Total minutes',
    getter: function(summary) {
      return secondsToMinutes(summary.durations.total);
    },
  },
  {
    header: 'Blocked minutes',
    getter: function(summary) {
      return secondsToMinutes(summary.durations.blocked_on_throttled_tree +
          summary.durations.blocked_on_closed_tree);
    },
  },
  {
    header: 'Failed jobs',
    getter: function(summary) {
      return summary.job_counts.failed;
    },
  },
  {
    header: 'Flaky jobs',
    getter: function(summary) {
      return summary.flaky_jobs.length;
    },
  },
  {
    header: 'Failed builders',
    getter: function(summary) {
      var builders = new Set();
      for (var attempt of summary.attempts) {
        for (var job of attempt.jobs.failed) {
          builders.add(job.master + ':' + job.builder);
        }
      }
      return builders.size;
    },
  },
];

})();
</script>
